package com.corpgovernment.permission;

import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.corpgovernment.common.common.CorpBusinessException;
import com.corpgovernment.organization.constant.PermissionConstant;
import com.corpgovernment.permission.entity.db.Menu;
import com.corpgovernment.permission.entity.db.MenuTemp;
import com.corpgovernment.permission.mapper.MenuTempMapper;
import com.corpgovernment.permission.vo.GetAllMenuTreeResponse;
import com.corpgovernment.permission.vo.MenuVo;
import com.corpgovernment.redis.cache.RedisUtils;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.Md5Utils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.corpgovernment.organization.constant.OrganizationResponseCodeEnum.MENU_IS_NULL;

/**
 * @author zl.cui
 * @date 2024/3/6
 */
@Component
@Slf4j
public class FlushMenuWorker {
    @Autowired
    private MenuTempMapper menuTempMapper;
    @Autowired
    private RedisUtils redisUtils;
    // 一共保存10个版本
    private static final int ROUND = 10;
    private static final String NOT_SYNC_SWITCH = "F";
    @Autowired
    @Qualifier(value = "flushMenuThreadPoolExecutor")
    private ThreadPoolExecutor flushMenuThreadPoolExecutor;
    @Scheduled(initialDelay = 0, fixedDelay = 1000 * 60 * 10)
    public void doTask() {
        log.info("FlushMenuWorker start");
        flushMenuThreadPoolExecutor.submit(this::flushMenu);
        log.info("FlushMenuWorker end");
    }

    private void flushMenu() {
        try{
        log.info("FlushMenuWorker start");
        String isSync = Config.defaultConfig().get("sync");
        // 没有开启同步开关，不同步菜单。
        if (StringUtils.isNotBlank(isSync) && NOT_SYNC_SWITCH.equals(isSync)) {
            return;
        }
        List<MenuVo> menuVoList = getMenu();
        if (CollectionUtils.isEmpty(menuVoList)) {
            return;
        }
        List<Menu> menuList = MenuConverter.toMenuVo(menuVoList);
        Map<String, Menu> map = menuList.stream().collect(Collectors.toMap(t -> String.valueOf(t.getId()), Function.identity()));

        MenuTemp menuTemp = menuTempMapper.getLastRecord();
        String newMenu = JsonUtils.toJsonString(menuList);
        if (StringUtils.isBlank(newMenu)){
            log.error("newMenu is null");
        }
        // 比对不一致才更新缓存
        if ((menuTemp == null || menuTemp.getMenuJson() == null ) || !Md5Utils.md5Hex(menuTemp.getMenuJson()).equals(Md5Utils.md5Hex(newMenu))) {
            redisUtils.delete(PermissionConstant.PERMISSION_LIST_PREFIX);
            redisUtils.setList(PermissionConstant.PERMISSION_LIST_PREFIX, menuList, 60 * 60 * 24 * 180);
            redisUtils.delete(PermissionConstant.PERMISSION_MAP_PREFIX);
            redisUtils.setHash(PermissionConstant.PERMISSION_MAP_PREFIX, map, 60 * 60 * 24 * 180);
            int count = menuTempMapper.selectCount(new MenuTemp());
            if (count >= ROUND) {
                MenuTemp oldestRecord = menuTempMapper.getTheOldestRecord();
                menuTempMapper.updateById(oldestRecord.getId(), newMenu);
            } else {
                MenuTemp newMenuTemp = new MenuTemp();
                newMenuTemp.setMenuJson(JsonUtils.toJsonString(menuList));
                newMenuTemp.setDataChangeLastTime(new Date());
                newMenuTemp.setDataChangeCreateTime(new Date());
                menuTempMapper.insert(newMenuTemp);
            }
        }
        }catch (Exception e){
            log.error("菜单同步异常", e);
        }
    }

    private List<MenuVo> getMenu(){
        log.info("getMenu start");
        String address = Config.defaultConfig().get("address");
        String env = Config.defaultConfig().get("env");
        String key = Config.defaultConfig().get("key");
        if (StringUtils.isBlank(env)){
            log.info("getMenu empty");
            return Collections.emptyList();
        }
        String result = null;
        try{
            Map<String, String> headers = new HashMap<>();
            headers.put("X-Api-Key", key);
            headers.put("Accept", "application/json");
            headers.put("Accept-Language", "zh-CN,zh;q=0.9");
            headers.put("Connection", "keep-alive");
            headers.put("Content-Type", "application/json;charset=UTF-8");
            headers.put("Env", env);
            HttpResponse response = HttpUtil.createPost(address).addHeaders(headers).body("{}").execute();
            if (response == null){
                return Collections.emptyList();
            }
            result = response.body();
            if (result == null){
                log.error("获取菜单地址失败");
            }
            log.info("获取菜单结果:{}", result);
        } catch (Exception e){
            log.error("getMenu exception:{}", e);
        }
        if (result == null){
            return Collections.emptyList();
        }
        com.ctrip.corp.obt.generic.core.http.HttpResponse<GetAllMenuTreeResponse> httpResponse = JsonUtils.parse(result, new TypeReference<com.ctrip.corp.obt.generic.core.http.HttpResponse<GetAllMenuTreeResponse>>() {
        });
        List<MenuVo> menuVoList = Optional.ofNullable(httpResponse).map(com.ctrip.corp.obt.generic.core.http.HttpResponse::getData)
                .map(GetAllMenuTreeResponse::getMenuList)
                .orElse(Collections.emptyList());
        if (CollectionUtils.isEmpty(menuVoList)){
           return null;
        }
        return flattenMenu(menuVoList);
    }
    private List<MenuVo> flattenMenu(List<MenuVo> menuList) {
        List<MenuVo> result = new ArrayList<>();
        for (MenuVo menu : menuList) {
            result.add(menu);
            if (menu.getChildren() != null && !menu.getChildren().isEmpty()) {
                result.addAll(flattenMenu(menu.getChildren()));
                menu.setChildren(null);
            }
        }
        return result;
    }
}
